#include <strings.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <google/protobuf/message.h>
#include <fstream>
#include <pcap.h>
#include <boost/filesystem.hpp>
#include <signal.h>

#include "multi.pb.h"
#include "hmac.h"
#include <openssl/hmac.h>
#include <netinet/ip.h>
#include <ctime>
#include "client.h"

#define UPLOAD_FOLDER "/diode/upload/"

#define MULTICASTIP "127.0.0.1"
#define CLIENT_MULTICASTPORT 3200
#define BLOB_SIZE 1000

bool g_running = true;

class udp_sender {
public:
	udp_sender(int port) {
		m_sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

		bzero(&m_servaddr,sizeof(m_servaddr));
		m_servaddr.sin_family = AF_INET;
		m_servaddr.sin_port=htons(port);
		m_servaddr.sin_addr.s_addr=inet_addr(MULTICASTIP);
	}
	void send(std::string data) {
		pthread_mutex_lock(&m_lock);
		sendto(m_sockfd,data.c_str(),data.size(),0,
		       (struct sockaddr *)&m_servaddr,sizeof(m_servaddr));

		pthread_mutex_unlock(&m_lock);
	}

	void send(server::Block const& block) {
		pthread_mutex_lock(&m_lock);
		std::string data;
		block.SerializeToString(&data);
		data.append(hmac(data));
		sendto(m_sockfd,data.c_str(),data.size(),0,
		       (struct sockaddr *)&m_servaddr,sizeof(m_servaddr));
		usleep(1000);

		pthread_mutex_unlock(&m_lock);
	}

private:
	int m_sockfd,m_n;
	struct sockaddr_in m_servaddr,m_cliaddr;
	pthread_mutex_t m_lock;
};

udp_sender sender(CLIENT_MULTICASTPORT);

void client(std::string const& fileName) {

	std::ifstream t(fileName.c_str());
	std::string str((std::istreambuf_iterator<char>(t)),
	                std::istreambuf_iterator<char>());

	unsigned int offset = 0;

	int count = 0;

	int fileID = rand();
	while(1) {
		std::string blob = str.substr(offset,BLOB_SIZE);
		server::Block block;

		block.mutable_data()->set_blob(blob);
		block.mutable_data()->set_seq(count);
		block.mutable_data()->set_fileid(fileID);
		block.mutable_data()->set_filename(fileName);
		block.mutable_data()->set_totalsize(str.size());

		sender.send(block);

		count++ ;

		offset += BLOB_SIZE;

		if(offset > str.size())
			break;
	}
/*
	{
		server::Block block;
		block.mutable_ctrl()->set_filename("a.out");
		block.mutable_ctrl()->set_runprogram(true);
		sender.send(block);
	}
	sleep(2);
	{
		server::Block block;
		block.mutable_ctrl()->set_filename("a.out");
		block.mutable_ctrl()->set_killprogram(true);
		sender.send(block);
	}*/
}


void* heartbeatSendThread(void *arg) {
	while(g_running) {
		server::Block block;
		unsigned int t = static_cast<unsigned int>(time(NULL));
		block.mutable_heartbeat()->set_time(t);
		sender.send(block);
		sleep(1);
	}
	return NULL;
}


Transmitter::Transmitter() {
	pthread_create(&(m_heartbeatProcess), NULL, &heartbeatSendThread, NULL);
}
void Transmitter::send(std::string const& file) {
	client(file);
}
void Transmitter::join() {
	pthread_join(m_heartbeatProcess, NULL);
}

void Transmitter::stop() {
	g_running = false;
//	pthread_kill(m_heartbeatProcess, SIGKILL);
}

/*
int main(int argc, char**argv) {
	srand (time(NULL));


	if(std::string(argv[1]) == "client") {
		Transmitter tx;
		tx.send(argv[2]);
		tx.join();
		//client(std::string(argv[2]));
	}

	if(std::string(argv[1]) == "dirscanner") {
	}

	return 0;
}*/
